package com.yanqu.road.server.manger.kowloon.battle;

import com.yanqu.road.entity.kowloon.battle.KowloonBattleData;
import com.yanqu.road.entity.kowloon.battle.KowloonBattleEnemyData;
import com.yanqu.road.entity.kowloon.battle.KowloonBattleHeroData;
import com.yanqu.road.entity.kowloon.common.Vec2;
import com.yanqu.road.entity.kowloon.enums.battle.eKowloonBulletType;
import com.yanqu.road.logic.model.kowloon.KowloonConfig;
import com.yanqu.road.server.manger.kowloon.battle.helper.KowloonEnemyDeadParam;
import com.yanqu.road.server.manger.kowloon.battle.role.bullet.*;
import com.yanqu.road.server.manger.kowloon.battle.role.enemy.KowloonEnemy;
import com.yanqu.road.server.manger.kowloon.battle.role.hero.KowloonHero;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class KowloonGame {

    private static KowloonGame game;

    public static KowloonGame getInstance() {
        if (game == null) {
            game = new KowloonGame();
        }
        return game;
    }

    public double gameTime;

    public List<KowloonHero> heros = new ArrayList<>();

    public List<KowloonEnemy> enemys = new ArrayList<>();

    public List<KowloonBullet> bullets = new ArrayList<>();

    public long randomSeed;

    public List<KowloonBattleEnemyData> enemyData = new ArrayList<>();

    public boolean gameOver = false;

    /**
     * 摊位血量
     */
    private int hp;

    /**
     * 记录下怪物造成的伤害
     */
    private Map<Long, Integer> hurtDamageMap = new ConcurrentHashMap<>();

    private Map<Long, KowloonEnemyDeadParam> deadParamMap = new ConcurrentHashMap<>();

    public Map<eKowloonBulletType, Deque<KowloonBullet>> bulletPool = new ConcurrentHashMap<>();

    private AtomicInteger id = new AtomicInteger(0);

    public int getHp() {
        return hp;
    }

    public void setHp(int hp) {
        this.hp = hp;
    }

    public Map<Long, Integer> getHurtDamageMap() {
        return hurtDamageMap;
    }

    public void beAttacked(long uniqueId, int damage) {
        damage = Math.min(damage, hp);
        this.hp = hp - damage;
        hurtDamageMap.put(uniqueId, hurtDamageMap.getOrDefault(uniqueId, 0) + damage);
    }

    public void recycleBullet(KowloonBullet bullet) {
        this.bulletPool.computeIfAbsent(bullet.getBulletType(), k -> new ArrayDeque<>());
        Deque<KowloonBullet> bulletList = this.bulletPool.get(bullet.getBulletType());
        bulletList.push(bullet);
    }

    public KowloonBullet getBullet(eKowloonBulletType bulletType) {
        Deque<KowloonBullet> bulletList = this.bulletPool.get(bulletType);
        if (bulletList != null && bulletList.size() > 0) {
            return bulletList.pop();
        }
        KowloonBullet bullet = null;
        switch (bulletType) {
            case Apple:
                bullet = new KowloonBulletApple(this);
                break;
            case Fish:
                bullet = new KowloonBulletFish(this);
                break;
            case Inkfish:
                bullet = new KowloonBulletInkfish(this);
                break;
            case Spear:
                bullet = new KowloonBulletSpear(this);
                break;
            case Amulet:
                bullet = new KowloonBulletAmulet(this);
                break;
            case Football:
                bullet = new KowloonBulletFootball(this);
                break;
            case Thunder:
                bullet = new KowloonBulletThunder(this);
                break;
            case Bee:
                bullet = new KowloonBulletBee(this);
                break;
            default:
                break;
        }
        return bullet;
    }

    public void setRandomSeed(long randomSeed) {
        this.randomSeed = randomSeed;
    }

    /**
     * 只能随机整数
     */
    public long random(int min, int max) {
        this.randomSeed = (214013 * this.randomSeed + 2531011) & 0x7fffffff;
        return min + this.randomSeed % (max - min + 1);
    }

    /**
     * 摇骰子，判断概率(千分比)
     */
    public boolean roll(int probability) {
        if (this.random(0, 999) < probability) {
            return true;
        }
        return false;
    }

    public void init(KowloonBattleData battleData) {
        this.gameTime = 0;
        this.id = new AtomicInteger(0);
        this.gameOver = false;
        this.heros = new ArrayList<>();
        this.enemys = new ArrayList<>();
        this.bullets = new ArrayList<>();
        this.hurtDamageMap = new ConcurrentHashMap<>();
        this.deadParamMap = new ConcurrentHashMap<>();
        this.bulletPool = new ConcurrentHashMap<>();
        this.setRandomSeed(battleData.getRandomSeed());
        for (KowloonBattleEnemyData battleEnemyData : battleData.getEnemyDataList()) {
            battleEnemyData.clone();
        }
        this.enemyData = battleData.getEnemyDataList();
        for (KowloonBattleHeroData battleHeroData : battleData.getHeroDataList()) {
            KowloonHero hero = KowloonHero.createHero(this, battleHeroData);
            this.heros.add(hero);
        }
        // 摊位血量
        this.hp = battleData.getHp();
    }

    public void start() {
        while (!this.gameOver) {
            // 防止死循环
            if (gameTime > 9999999) {
                return;
            }
            this.update(KowloonConfig.FRAME_TIME);
        }
    }

    public void update(double dt) {
        this.addEnemyGroup();
        ArrayList<KowloonEnemy> kowloonEnemies = new ArrayList<>(enemys);
        for (KowloonEnemy enemy : kowloonEnemies) {
            enemy.update(dt);
        }
        ArrayList<KowloonHero> kowloonHeroes = new ArrayList<>(heros);
        for (KowloonHero hero : kowloonHeroes) {
            hero.update(dt);
        }
        ArrayList<KowloonBullet> kowloonBullets = new ArrayList<>(bullets);
        for (KowloonBullet bullet : kowloonBullets) {
            bullet.update(dt);
            bullet.checkOutOfScene();
        }
        // 删除销毁的子弹
        this.bullets.removeIf(next -> next.isDestroyed);
        this.gameTime += dt * 1000;
        this.gameOver = this.checkGameOver();
    }

    /**
     * 检查游戏是否结束
     */
    public boolean checkGameOver() {
        // 摊位炸了
        if (this.hp <= 0) {
            return true;
        }
        if (this.enemyData.size() > 0) {
            return false;
        }
        for (KowloonEnemy enemy : enemys) {
            if (enemy.getHp() > 0) {
                return false;
            }
        }
        return true;
    }

    /** 添加怪物 */
    public void addEnemyGroup() {
        for (int i = this.enemyData.size() - 1; i >= 0; i--) {
            KowloonBattleEnemyData data = this.enemyData.get(i);
            if (data.getBornTime() <= this.gameTime) {
                this.addEnemy(data);
                this.enemyData.remove(data);
            }
        }
    }

    public void addEnemy(KowloonBattleEnemyData enemyData) {
        this.enemys.add(new KowloonEnemy(this, enemyData));
    }

    public void addBullet(KowloonBullet bullet) {
        this.bullets.add(bullet);
    }

    /**
     * 根据列行获取坐标 （这个坐标不是实际的坐标，绘制的时候再转换）
     */
    public Vec2 getPos(double col, double row) {
        double x = (col + 0.5) * KowloonConfig.TILE_SIZE;
        double y = (row + 0.5) * KowloonConfig.TILE_SIZE;
        return new Vec2(x, y);
    }

    /** 算两点的距离 */
    public double distance(Vec2 point1, Vec2 point2) {
        return Math.pow(Math.pow(point2.getX() - point1.getX(), 2) + Math.pow(point2.getY() - point1.getY(), 2), 0.5);
    }

    public int getId() {
        return id.incrementAndGet();
    }

    public static KowloonGame getGame() {
        return game;
    }

    public double getGameTime() {
        return gameTime;
    }

    public List<KowloonHero> getHeros() {
        return heros;
    }

    public List<KowloonEnemy> getEnemys() {
        return enemys;
    }

    public List<KowloonBullet> getBullets() {
        return bullets;
    }

    public long getRandomSeed() {
        return randomSeed;
    }

    public List<KowloonBattleEnemyData> getEnemyData() {
        return enemyData;
    }

    public boolean isGameOver() {
        return gameOver;
    }

    public Map<eKowloonBulletType, Deque<KowloonBullet>> getBulletPool() {
        return bulletPool;
    }

    public Map<Long, KowloonEnemyDeadParam> getDeadParamMap() {
        return deadParamMap;
    }

    public void setDeadParamMap(Map<Long, KowloonEnemyDeadParam> deadParamMap) {
        this.deadParamMap = deadParamMap;
    }
}
